#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include "psd_xfer.h"

static LIST_HEAD(xfer_listhead, xfer_args_t) xfer_args_list;
static pthread_mutex_t xfer_list_lock;
static int xfer_count;

int psd_xfer_list_init() {
	xfer_count = 0;
	LIST_INIT(&xfer_args_list);

	return 0;
}

int psd_register_xfer_arg(psdXferArgs *arg) {
        pthread_mutex_lock(&xfer_list_lock);
        {
		LIST_INSERT_HEAD(&xfer_args_list, arg, xfer_entries);
		xfer_count++;
        }
        pthread_mutex_unlock(&xfer_list_lock);

        return 0;
}

int psd_unregister_xfer_arg(psdXferArgs *arg) {
	pthread_mutex_lock(&xfer_list_lock);
        {
		if (arg->xfer_entries.le_prev != NULL) {
			LIST_REMOVE(arg, xfer_entries);
			xfer_count--;
		}
        }
        pthread_mutex_unlock(&xfer_list_lock);

        return 0;
}

psdXferArgs **psd_get_xfer_args(int *count) {
        psdXferArgs *curr_arg;
        psdXferArgs **xfer_list_ret;

        pthread_mutex_lock(&xfer_list_lock);
        {
                xfer_list_ret = malloc(sizeof(psdXferArgs *) * xfer_count);
                if (xfer_list_ret) {
                        int i = 0;
			LIST_FOREACH(curr_arg, &xfer_args_list, xfer_entries) {
                                xfer_list_ret[i] = curr_arg;
                                i++;
                        }
			
                        *count = xfer_count;
                }
        }
        pthread_mutex_unlock(&xfer_list_lock);

        return xfer_list_ret;
}

psdXferArgs *psd_alloc_xfer_args_set(psdSess *sess, psdConn *src, psdConn *sink, int bufsize) {
	psdXferArgs *args;
	
	args = psd_alloc_xfer_args();
	if (!args)
		goto error_exit;

	/* allocating the buffer is now done in the transfer backend */
	//args->buf = malloc(sizeof(char) * bufsize);
	//if (!args->buf)
	//	goto error_exit2;

	args->sess = psd_session_get_ref(sess);
	args->src = src;
	args->sink = sink;
	args->bufsize = bufsize;

	return args;

error_exit2:
	psd_free_xfer_args(args);
error_exit:
	return NULL;
}

psdXferArgs *psd_alloc_xfer_args(void) {
	psdXferArgs *args;

	args = malloc(sizeof(psdXferArgs));
	if (!args)
		goto error_exit;

	bzero(args, sizeof(psdXferArgs));

	if (pthread_mutex_init(&(args->buf_lock), NULL) < 0)
		goto error_exit2;

	if (pthread_mutex_init(&(args->lock), NULL) < 0)
		goto error_exit2;

	if (pthread_mutex_init(&(args->sink_lock), NULL) < 0)
                goto error_exit2;

	if (pthread_cond_init(&(args->buf_cond), NULL) < 0)
                goto error_exit3;

	if (pthread_cond_init(&(args->cond), NULL) < 0)
		goto error_exit3;
	
	if (pthread_cond_init(&(args->sink_cond), NULL) < 0)
                goto error_exit3;

	// so if close gets called on this before it's set, it doesn't close down fd0
	args->parent_fd = -1;

	return args;

error_exit3:
	pthread_mutex_destroy(&(args->lock));
error_exit2:
	free(args);
error_exit:
	return NULL;
}

void psd_free_xfer_args(psdXferArgs *args) {
	if (args->buf != NULL)
		free(args->buf);
	if (args->sess != NULL)
		psd_session_put_ref(args->sess);

	pthread_cond_destroy(&(args->buf_cond));
	pthread_cond_destroy(&(args->cond));
	pthread_cond_destroy(&(args->sink_cond));
	pthread_mutex_destroy(&(args->buf_lock));
	pthread_mutex_destroy(&(args->lock));
	pthread_mutex_destroy(&(args->sink_lock));
	free(args);
}
